﻿/*
 * Created by Javier Cantón Ferrero.
 * MVP Windows-DirectX 2007/2008
 * MSP 2006/2008
 * Date 22/06/2008
 * Web www.codeplex.com/XNACommunity
 * Email javiuniversidad@gmail.com
 * blog: mirageproject.blogspot.com
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Material.Shader;
using Microsoft.Xna.Framework.Graphics;
using Engine.Utils.Atributes;
using Microsoft.Xna.Framework;
using Engine.Modelo;
using Engine.Cameras;
using Engine.Components;
using Engine;
using Engine.Publishers;
using Engine.Commands;
namespace Engine.Features.Water
{
    [Component("Water", false)]
    [Services("Engine.EngineStuff", Escopo = Escope.SINGLETON)]
    public class DinamicWater :  IComponent
    {
        private RenderTarget2D refractionRT;
        private Texture2D refractionMap;
        private RenderTarget2D reflectionRT;
        private Texture2D reflectionMap;
        private Matrix reflectionViewMatrix;
        public GraphicsDevice device;
        public float waterHeight;
        private int WIDTH;
        private int HEIGHT;
        public Vector3 position;
        private Effect effect;
        private Texture2D waterBumpMap;
        private VertexPositionTexture[] waterVertices;
        public delegate void RenderRefractionHandler();
        public delegate void RenderReflectionHandler(Matrix view, Vector3 reflectionCamPosition);
        ICamera _camera;
        public static readonly String MyName = "Water";
        EngineStuff _es;
        bool initialized = false;
        private ISubject _sub = new ConcreteSubject();
        public DinamicWater(EngineStuff es)
            
        {        
            this._es = es;

            SetUpWaterVertices();
        }

        public void setParameters(ICamera cam)
        {
            this._camera = cam;
        }
        public void setParameters(ICamera cam,int width, int height, Vector3 position, float waterHeight)
        {
            this.WIDTH = width;
            this.HEIGHT = height;
            this.position = position;
            this.waterHeight = waterHeight;
            this._camera = cam;
            SetUpWaterVertices();
        }
       
        //public void Initialize(GraphicsDevice device, ContentManager content)
        public  void Initialize()
        {
            initialized = true;
            device = _es.GraphicsDevice;   
            refractionRT = new RenderTarget2D(device, 512, 512, 1, SurfaceFormat.Color);
            reflectionRT = new RenderTarget2D(device, 512, 512, 1, SurfaceFormat.Color);
            effect = _es.Content.Load<Effect>("Effects/Water");
            waterBumpMap = _es.Content.Load<Texture2D>("Textures/waterbump");
            
        }

        private void SetUpWaterVertices()
        {
            waterVertices = new VertexPositionTexture[6];

            waterVertices[0] = new VertexPositionTexture(new Vector3(0, waterHeight, 0), new Vector2(0, 1));
            waterVertices[1] = new VertexPositionTexture(new Vector3(WIDTH, waterHeight, HEIGHT), new Vector2(1, 0));
            waterVertices[2] = new VertexPositionTexture(new Vector3(0, waterHeight, HEIGHT), new Vector2(0, 0));

            waterVertices[3] = new VertexPositionTexture(new Vector3(0, waterHeight, 0), new Vector2(0, 1));
            waterVertices[4] = new VertexPositionTexture(new Vector3(WIDTH, waterHeight, 0), new Vector2(1, 1));
            waterVertices[5] = new VertexPositionTexture(new Vector3(WIDTH, waterHeight, HEIGHT), new Vector2(1, 0));
        }

        public void DrawRefractionMap(ICamera cam, RenderRefractionHandler render)
        {

            Vector3 planeNormalDirection = new Vector3(0, -1, 0);
            planeNormalDirection.Normalize();
            Vector4 planeCoefficients = new Vector4(planeNormalDirection, waterHeight);

            Matrix camMatrix = cam.View *cam.Projection;
            Matrix invCamMatrix = Matrix.Invert(camMatrix);
            invCamMatrix = Matrix.Transpose(invCamMatrix);

            planeCoefficients = Vector4.Transform(planeCoefficients, invCamMatrix);
            Plane refractionClipPlane = new Plane(planeCoefficients);

            device.ClipPlanes[0].Plane = refractionClipPlane;
            device.ClipPlanes[0].IsEnabled = true;

            device.SetRenderTarget(0, refractionRT);
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

            //RenderScene
            render.Invoke();

            device.SetRenderTarget(0, null);
            refractionMap = refractionRT.GetTexture();
            device.ClipPlanes[0].IsEnabled = false;
        }
        public Matrix UpdateReflectiveMatrices(ICamera cam)
        {
            Matrix reflectiveViewMatrix;
            reflectiveViewMatrix = Matrix.Identity;
            reflectiveViewMatrix *= Matrix.CreateTranslation(new Vector3(-cam.Position.X, cam.Position.Y - 10f, -cam.Position.Z));
            reflectiveViewMatrix *= Matrix.CreateRotationZ(cam.Rotation.Z);
            reflectiveViewMatrix *= Matrix.CreateRotationY(cam.Rotation.Y);
            reflectiveViewMatrix *= Matrix.CreateRotationX(-cam.Rotation.X);
            return reflectiveViewMatrix;
        }

        public void DrawReflectionMap(ICamera cam, RenderReflectionHandler render)
        {
            reflectionViewMatrix = UpdateReflectiveMatrices(cam);
            float reflectionCamYCoord = -cam.Position.Y + (waterHeight * 2);
            Vector3 reflectionCamPosition = new Vector3(cam.Position.X, reflectionCamYCoord, cam.Position.Z);

            Vector3 planeNormalDirection = new Vector3(0, 1, 0);
            planeNormalDirection.Normalize();
            Vector4 planeCoefficients = new Vector4(planeNormalDirection, -3f);

            Matrix camMatrix = reflectionViewMatrix * cam.Projection;
            Matrix invCamMatrix = Matrix.Invert(camMatrix);
            invCamMatrix = Matrix.Transpose(invCamMatrix);

            planeCoefficients = Vector4.Transform(planeCoefficients, invCamMatrix);
            Plane refractionClipPlane = new Plane(planeCoefficients);

            device.ClipPlanes[0].Plane = refractionClipPlane;
            device.ClipPlanes[0].IsEnabled = true;
            device.SetRenderTarget(0, reflectionRT);
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

            //RenderScene
            render.Invoke(reflectionViewMatrix, reflectionCamPosition);

            device.SetRenderTarget(0, null);
            reflectionMap = reflectionRT.GetTexture();

            device.ClipPlanes[0].IsEnabled = false;
        }
        private void RenderRefraction() { }
        private void RenderReflection(Matrix view, Vector3 reflectionCamPosition) { }

        public  void Draw(GameTime gameTime)
        {
            if (!initialized || _camera == null)
            {
                return;
            }
            //DrawRefractionMap(_camera, new DinamicWater.RenderRefractionHandler(RenderRefraction));
            //DrawReflectionMap(_camera, new DinamicWater.RenderReflectionHandler(RenderReflection));
            DrawWater(_camera, gameTime);
        }

        public void DrawWater(ICamera camera, GameTime gameTime)
        {
            effect.CurrentTechnique = effect.Techniques["Water"];
            Matrix worldMatrix = Matrix.CreateTranslation(position - new Vector3(WIDTH / 2, 0, HEIGHT / 2));

            effect.Parameters["View"].SetValue(camera.View);
            effect.Parameters["Projection"].SetValue(camera.Projection);
            effect.Parameters["World"].SetValue(worldMatrix);
            effect.Parameters["View"].SetValue(camera.View);
            effect.Parameters["Projection"].SetValue(camera.Projection);
            effect.Parameters["ReflectionView"].SetValue(reflectionViewMatrix);
            effect.Parameters["WaterBumpMap"].SetValue(waterBumpMap);
            effect.Parameters["ReflectionMap"].SetValue(reflectionMap);
            effect.Parameters["RefractionMap"].SetValue(refractionMap);
            effect.Parameters["waveLength"].SetValue(0.1f);
            effect.Parameters["waveHeight"].SetValue(0.3f);
            effect.Parameters["CamPos"].SetValue(camera.Position);
            effect.Parameters["dullColor"].SetValue(Color.LightCyan.ToVector4());
            effect.Parameters["dullBlendFactor"].SetValue(0.2f);
            float elapsedTime = (float)gameTime.TotalGameTime.TotalMilliseconds / 100000.0f;
            effect.Parameters["Time"].SetValue(elapsedTime);
            effect.Parameters["windDirection"].SetValue(Matrix.CreateRotationZ(MathHelper.PiOver2));

            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                device.VertexDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements);
                device.DrawUserPrimitives(PrimitiveType.TriangleList, waterVertices, 0, 2);
                pass.End();
            }
            effect.End();

        }


        #region IReciever Members

        public string getMyName()
        {
            return MyName;
        }

        #endregion

        #region IEntity Members

        private int _id;

        public int getId()
        {
            return _id;
        }

        public void setId(int id)
        {
            this._id = id;
        }

        #endregion

        #region ISubject Members
        public int ObserverNumber()
        {
            return _sub.ObserverNumber();
        }

        public void Attach(IObserver obs)
        {
            _sub.Attach(obs);
        }

        public void Dettach(IObserver obs)
        {
            _sub.Dettach(obs);
        }

        public void DettachAll()
        {
            _sub.DettachAll();
        }

        public void Notify(object obj)
        {
            _sub.Notify(obj);
        }

        #endregion

        #region IComponent Members


        public void Update(GameTime gt)
        {
            throw new NotImplementedException();
        }

        public void PreDraw(GameTime gt)
        {
            throw new NotImplementedException();
        }

        public void AfterDraw(GameTime gt)
        {
            throw new NotImplementedException();
        }

        public void LoadContent(EngineStuff engine)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public ComponentType ComponentType
        {
            get { throw new NotImplementedException(); }
        }

        #endregion
    }
}
